﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//1.几个经典的笔试题

//题目一：

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");//此时str还是空指针
	printf(str);
}
int main()
{
	test();
	return;
}
//请问运行Test函数会有什么样的结果？
//程序直接挂掉了
//1.str传给p的时候，p是str的临时拷贝，有自己独立的空间，当getmemory函数申请了空间后，
//地址挡在p中时，str依然是空，当getmemory返回后，strcpy拷贝时，形成了非法访问内存
//2.在getmemory函数内部申请了动态内存，但是没有释放，会发生内存泄露



//题目二：

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}
int main()
{
	Test();
	return 0;
}
//请问运行Test函数会有什么样的结果？
//返回栈空间地址的问题
//运行结果是随机的，因为在GetMemory函数中创建的数组
//在离开这个函数之后就没有了（还给操作系统了），所以会造成非法访问


//题目三：

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello world");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

//请问运行Test函数会有什么样的结果
//会存在内存泄漏，因为使用了malloc，没有free进行内存的释放

//题目四：

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}
//请问运行Test函数会有什么样的结果
//非法访问并且str在释放之后是野指针


//2.C / C++程序的内存开辟

//C / C++程序内存分配的几个区域：
//1. 栈区（stack）：在执行函数时，函数内局部变量的存储单元都可以在栈上创建，函数执行结束时这些
//存储单元自动被释放。栈内存分配运算内置于处理器的指令集中，效率很高，但是分配的内存容量有
//限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
//2. 堆区（heap）：一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。分配方式类似
//于链表。
//3. 数据段（静态区）（static）存放全局变量、静态数据。程序结束后由系统释放。
//4. 代码段：存放函数体（类成员函数和全局函数）的二进制代码。

//综上所述：
//实际上普通的局部变量是在栈区分配空间的，栈区的特点是在上面创建的变量出了作用域就销毁。
//但是被static修饰的变量存放在数据段（静态区），数据段的特点是在上面创建的变量，
//直到程序结束才销毁所以生命周期变长。


//3.柔性数组
//也许你从来没有听说过柔性数组（ﬂexible array）这个概念，但是它确实是存在的。 C99标准中，结构中的最
//后一个元素允许是未知大小的数组，这就叫做『柔性数组』成员。

//例如：
typedef struct st_type
{
	int i;
	int a[0];//柔性数组成员，数组大小未知
}type_a;

//有些编译器会报错无法编译可以改成：
typedef struct st_type
{
	int i;
	int a[];//柔性数组成员，数组大小未知
}type_a;


//3.1柔性数组的特点： 
//1.结构中的柔性数组成员前面必须至少一个其他成员
//2.sizeof 返回的这种结构大小不包括柔性数组的内存，也就是在计算结构体大小的时候，不不含柔性数组的大小
//3.包含柔性数组成员的结构用malloc()函数进行内存的动态分配，并且分配的内存应该大于结构的大小，以适应柔性数组的预期大小。

//特点2例如：
typedef struct st_type
{
	int i;
	int a[0];//柔性数组成员
}type_a;
int main()
{
	printf("%d\n", sizeof(type_a));//输出的是4，仅包含前面int类型大小
	return 0;
}

//特点3例如：
struct s
{
	int i;//4
	char arr[];//数组大小未知，柔性数组
};
int main()
{
	//给柔性数组开辟空间,开辟10个char类型大小
	struct s* sp = (struct s*)malloc(sizeof(struct s) + 10 * sizeof(char));
	//使用：
	sp->i = 100;
	for (int i = 0; i < 10; i++)
	{
		sp->arr[i] = 'Q';
	}
	//打印：
	for (int i = 0; i < 10; i++)
	{
		printf("%c ", sp->arr[i]);
	}
	//释放
	free(sp);
	sp = NULL;
	return 0;
}


//柔性数组的使用
//代码1
typedef struct st_type
{
	int i;
	int a[0];//柔性数组成员
}type_a;
int main()
{
	int i = 0;
	type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
	//业务处理
	p->i = 100;
	for (i = 0; i < 100; i++)
	{
		p->a[i] = i;
	}
	free(p);
	return 0;
}
//这样柔性数组成员a，相当于获得了100个整型元素的连续空间。

//代码2
typedef struct st_type
{
	int i;
	int* p_a;
}type_a;
int main()
{
	type_a* p = malloc(sizeof(type_a));
	p->i = 100;
	p->p_a = (int*)malloc(p->i * sizeof(int));

	//业务处理
	for (int i = 0; i < 100; i++)
	{
		p->p_a[i] = i;
	}

	//释放空间
	free(p->p_a);
	p->p_a = NULL;
	free(p);
	p = NULL;
	return 0;
}

//上述 代码1 和 代码2 可以完成同样的功能，但是 方法1 的实现有两个好处： 

//第一个好处是：方便内存释放,如果我们的代码是在一个给别人用的函数中，你在里面做了二次内存分配，
//并把整个结构体返回给用户。用户调用free可以释放结构体，但是用户并不知道这个结构体内的成员也需要free
//所以你不能指望用户来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存一次性分配好了
//并返回给用户一个结构体指针，用户做一次free就可以把所有的内存也给释放掉。

//第二个好处是：这样有利于访问速度:连续的内存有益于提高访问速度，也有益于减少内存碎片





